frontend/pages/e/[uuid].tsx (view raw)
1import {useState, useReducer, useEffect} from 'react';
2import Box from '@material-ui/core/Box';
3import {makeStyles} from '@material-ui/core/styles';
4import {useTranslation} from 'react-i18next';
5import {initializeApollo} from '../../lib/apolloClient';
6import useToastStore from '../../stores/useToastStore';
7import useEventStore from '../../stores/useEventStore';
8import Layout from '../../layouts/Default';
9import AddToMyEventDialog from '../../containers/AddToMyEventDialog';
10import TravelColumns from '../../containers/TravelColumns';
11import NewTravelDialog from '../../containers/NewTravelDialog';
12import VehicleChoiceDialog from '../../containers/VehicleChoiceDialog';
13import WelcomeDialog from '../../containers/WelcomeDialog';
14import EventBar from '../../containers/EventBar';
15import Loading from '../../containers/Loading';
16import OnBoardingTour from '../../containers/OnBoardingTour';
17import {
18 useUpdateEventMutation,
19 Event as EventType,
20 useEventByUuidQuery,
21 EventByUuidDocument,
22 EditEventInput,
23 useFindUserVehiclesQuery,
24} from '../../generated/graphql';
25import ErrorPage from '../_error';
26import useProfile from '../../hooks/useProfile';
27import Fab from '../../containers/Fab';
28
29const POLL_INTERVAL = 10000;
30
31interface Props {
32 event: EventType;
33 eventUUID: string;
34}
35
36const EventPage = props => {
37 const {t} = useTranslation();
38 const {event} = props;
39 if (!event) return <ErrorPage statusCode={404} title={t`event.not_found`} />;
40 return <Event {...props} />;
41};
42
43const Event = (props: Props) => {
44 const {eventUUID} = props;
45 const classes = useStyles();
46 const {t} = useTranslation();
47 const {user} = useProfile();
48 const {
49 data: {me: {profile: {vehicles = []} = {}} = {}} = {},
50 loading
51 } = useFindUserVehiclesQuery();
52 const addToast = useToastStore(s => s.addToast);
53 const setEvent = useEventStore(s => s.setEvent);
54 const eventUpdate = useEventStore(s => s.event);
55 const setIsEditing = useEventStore(s => s.setIsEditing);
56 const [updateEvent] = useUpdateEventMutation();
57 const [isAddToMyEvent, setIsAddToMyEvent] = useState(false);
58 const [openNewTravelContext, toggleNewTravel] = useState({opened: false});
59 const [openVehicleChoice, toggleVehicleChoice] = useReducer(i => !i, false);
60 const {data: {eventByUUID: event} = {}} = useEventByUuidQuery({
61 pollInterval: POLL_INTERVAL,
62 variables: {uuid: eventUUID},
63 });
64
65 useEffect(() => {
66 if (event) setEvent(event as EventType);
67 }, [event]);
68
69 const onSave = async e => {
70 try {
71 const {uuid, ...data} = eventUpdate;
72 const {id, __typename, travels, users, waitingList, ...input} = data;
73 await updateEvent({
74 variables: {uuid, eventUpdate: input as EditEventInput},
75 refetchQueries: ['eventByUUID'],
76 });
77 setIsEditing(false);
78 } catch (error) {
79 console.error(error);
80 addToast(t('event.errors.cant_update'));
81 }
82 };
83
84 const onShare = async () => {
85 if (!event) return null;
86 // If navigator share capability
87 if (!!navigator.share)
88 return await navigator.share({
89 title: `Caroster ${event.name}`,
90 url: `${window.location.href}`,
91 });
92 // Else copy URL in clipboard
93 else if (!!navigator.clipboard) {
94 await navigator.clipboard.writeText(window.location.href);
95 addToast(t('event.actions.copied'));
96 return true;
97 }
98 };
99
100 const addTravelClickHandler =
101 user && vehicles?.length != 0
102 ? toggleVehicleChoice
103 : () => toggleNewTravel({opened: true});
104
105 if (!event || loading) return <Loading />;
106
107 return (
108 <Layout
109 pageTitle={t('event.title', {title: event.name})}
110 menuTitle={t('event.title', {title: event.name})}
111 displayMenu={false}
112 >
113 <EventBar
114 event={event}
115 onAdd={setIsAddToMyEvent}
116 onSave={onSave}
117 onShare={onShare}
118 />
119 <TravelColumns toggle={addTravelClickHandler} />
120 <Box className={classes.bottomRight}>
121 <Fab
122 onClick={addTravelClickHandler}
123 aria-label="add-car"
124 color="primary"
125 >
126 {t('travel.creation.title')}
127 </Fab>
128 </Box>
129 <NewTravelDialog
130 context={openNewTravelContext}
131 toggle={() => toggleNewTravel({opened: false})}
132 />
133 <VehicleChoiceDialog
134 open={openVehicleChoice}
135 toggle={toggleVehicleChoice}
136 toggleNewTravel={toggleNewTravel}
137 vehicles={vehicles}
138 />
139 <AddToMyEventDialog
140 event={event}
141 open={isAddToMyEvent}
142 onClose={() => setIsAddToMyEvent(false)}
143 />
144 <WelcomeDialog />
145 <OnBoardingTour />
146 </Layout>
147 );
148};
149
150export async function getServerSideProps(ctx) {
151 const {uuid} = ctx.query;
152 const apolloClient = initializeApollo();
153 const {data = {}} = await apolloClient.query({
154 query: EventByUuidDocument,
155 variables: {uuid},
156 });
157 const {eventByUUID: event} = data;
158 const {host = ''} = ctx.req.headers;
159
160 return {
161 props: {
162 event,
163 eventUUID: uuid,
164 metas: {
165 title: event?.name || '',
166 url: `https://${host}${ctx.resolvedUrl}`,
167 },
168 },
169 };
170}
171
172const useStyles = makeStyles(theme => ({
173 bottomRight: {
174 position: 'absolute',
175 bottom: theme.spacing(1),
176 right: theme.spacing(6),
177 width: 200,
178 [theme.breakpoints.down('sm')]: {
179 right: theme.spacing(1),
180 },
181 },
182}));
183
184export default EventPage;